﻿using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.DistributedLocking;
using Volo.Abp.Guids;
using Volo.Abp.Json;
using Volo.Abp.Modularity;
using Volo.Abp.Reflection;
using Volo.Abp.Threading;
using Volo.Abp.Timing;
using Xms.Abp.Modularity;
using Xms.Abp.MultiTenancy;

namespace Xms.Abp.BackgroundJob;

[DependsOn(
    typeof(AbpTimingModule),
    typeof(AbpGuidsModule),
    typeof(AbpDistributedLockingAbstractionsModule),
    typeof(AbpJsonModule),
    typeof(AbpThreadingModule),
    typeof(XmsAbpMultiTenancyModule)
    )]
public class XmsAbpBackgroundJobsModule : XmsModule
{
    public override void PreConfigureServices(ServiceConfigurationContext context)
    {
        RegisterJobs(context.Services);
    }

    private static void RegisterJobs(IServiceCollection services)
    {
        var jobTypes = new List<Type>();
        var scheduleTypes = new List<Type>();

        services.OnRegistred(context =>
        {
            if (ReflectionHelper.IsAssignableToGenericType(context.ImplementationType, typeof(IBackgroundJob<>)))
            {
                jobTypes.Add(context.ImplementationType);
            }
            else if (context.ImplementationType.IsAssignableTo(typeof(IBackgroundSchedule)))
            {
                if (context.ImplementationType != typeof(DynamicBackgroundSchedule))
                {
                    scheduleTypes.Add(context.ImplementationType);
                }
            }
        });

        services.Configure<XmsBackgroundJobTriggerOptions>(options =>
        {
            foreach (var jobType in jobTypes)
            {
                options.AddJob(jobType);
            }
        });

        services.Configure<XmsBackgroundScheduleTriggerOptions>(options =>
        {
            foreach (var scheduleType in scheduleTypes)
            {
                options.AddSchedule(scheduleType);
            }
        });
    }

    public override void PostConfigureServices(ServiceConfigurationContext context)
    {
        base.PostConfigureServices(context);
        var serv = context.Services.First(r => r.ImplementationType == typeof(DynamicBackgroundSchedule));
        context.Services.Remove(serv);
        context.Services.AddTransient<DynamicBackgroundSchedule>();
    }

    public override async Task OnPostApplicationInitializationAsync(ApplicationInitializationContext context)
    {
        await base.OnPostApplicationInitializationAsync(context);
        var options = context.ServiceProvider.GetRequiredService<IOptions<XmsBackgroundJobTriggerOptions>>().Value;
        if (options.IsJobExecutionEnabled)
        {
            var jobTrigger = context.ServiceProvider.GetRequiredService<IBackgroundJobTrigger>();
            await jobTrigger.StartAsync();
        }
        var scheduleOptions = context.ServiceProvider.GetRequiredService<IOptions<XmsBackgroundScheduleTriggerOptions>>().Value;
        if (scheduleOptions.IsScheduleExecutionEnabled)
        {
            await context.ServiceProvider
                .GetRequiredService<IBackgroundScheduleManageTrigger>()
                .StartAsync();
        }
    }

    public async override Task OnApplicationShutdownAsync(ApplicationShutdownContext context)
    {
        var options = context.ServiceProvider.GetRequiredService<IOptions<XmsBackgroundScheduleTriggerOptions>>().Value;
        if (options.IsScheduleExecutionEnabled)
        {
            await context.ServiceProvider
                .GetRequiredService<IBackgroundScheduleManageTrigger>()
                .StopAsync();
        }
    }

    public override void OnPostApplicationInitialization(ApplicationInitializationContext context)
    {
        base.OnPostApplicationInitialization(context);
        AsyncHelper.RunSync(() => OnPostApplicationInitializationAsync(context));
    }

    public override void OnApplicationShutdown(ApplicationShutdownContext context)
    {
        base.OnApplicationShutdown(context);
        AsyncHelper.RunSync(() => OnApplicationShutdownAsync(context));
    }
}
